
# 缺省的模块安装
# pip install pyserial -i https://pypi.tuna.tsinghua.edu.cn/simple
# pip install keyboard -i https://pypi.tuna.tsinghua.edu.cn/simple


# pip install python-uinput -i https://pypi.tuna.tsinghua.edu.cn/simple

# -i http://mirrors.aliyun.com/pypi/simple
# -i https://pypi.tuna.tsinghua.edu.cn/simple

# 彩色输出
# \x1b[30m - 黑
# \x1b[31m - 红
# \x1b[32m - 绿
# \x1b[33m - 黄
# \x1b[34m - 蓝
# \x1b[35m - 紫
# \x1b[36m - 天蓝
# \x1b[37m - 白

import serial
import sys
import os
import threading
import time
import keyboard
import platform


#from pykeyboard import InlineKeyboard
#from pykeyboard.hotkey import Hotkey, HotkeyListener
#from pynput import keyboard

# 获取操作系统类型
def get_os():
    if os.name == 'nt':
        return 'Windows'
    elif os.name == 'posix':
        if 'darwin' in platform.system().lower():
            return 'macOS'
        else:
            return 'Linux'
    else:
        return 'Unknown'

run_flag = 1
# 默认参数
if get_os() == 'Windows':
    port ='COM3'
elif get_os() == 'Linux':
    port = '/dev/ttyUSB0'
    
baudrate = 9600
encoding = 'utf-8'

# 串口接收显示线程任务
def serial_Receive():
    global run_flag
    global encoding
    try:
        while True:
            if (run_flag == 1) and ser.is_open:
                # 等待串口接收,终端显示，忽略接收异常字符避免崩溃退出
                line_str = ser.readline().decode(encoding = encoding, errors = 'ignore')
                if line_str:
                    sys.stdout.write(line_str)
                    sys.stdout.flush()
            else:
                break
                
    except Exception as e:
        print('\x1b[31m错误', e, '\x1b[37m')
        run_flag = 0
        
    # ctrl+c 按下
    except KeyboardInterrupt:
        print('\x1b[33m快捷键按下: ctrl+c\x1b[37m')
        run_flag = 0
        pass
    
# 控制台输入处理线程任务
def input_Send():
    global run_flag
    global encoding
    try:
        while True:
            if (run_flag == 1):
                # 等待用户输入，发送到串口
                line_str = sys.stdin.readline()
                line_bytes = line_str.encode(encoding)
                if line_bytes and ser.is_open:
                    ser.write(line_bytes)
                    
                # 对输入进行处理，如按 Ctrl+C 或者 'exit' 退出
                #if line_str and "exit" in line_str.strip():
                if line_str and line_str.strip() == 'exit':
                    print('\x1b[33mexit 退出')
                    run_flag = 0
                    break
                # 清空控制台
                elif line_str and line_str.strip() == 'clear':
                    if get_os() == 'Windows':
                        os.system('cls')
                    elif get_os() == 'Linux':
                        os.system('clear')
                    elif get_os() == 'macOS':
                        os.system('clear')
            else:
                break
                
    except Exception as e:
        print('\x1b[31m错误', e, '\x1b[37m')
        run_flag = 0
        
    # ctrl+c 按下
    except KeyboardInterrupt:
        print('\x1b[33m快捷键按下: ctrl+c\x1b[37m')
        run_flag = 0
        pass

# 热键回调
def hotkey_ctrl_close_callBack():
    global run_flag
    run_flag = 0
    if keyboard.is_pressed('ctrl+]'):
        print('\x1b[33m快捷键按下: ctrl+]\x1b[37m')
    elif keyboard.is_pressed('ctrl+z'):
        print('\x1b[33m快捷键按下: ctrl+z\x1b[37m')


def main():
    global run_flag
    global encoding
    global baudrate
    global port
    global ser
    
    # 创建线程
    thread_read = threading.Thread(target=serial_Receive)
    thread_write = threading.Thread(target=input_Send)
    
    # 打印串口模块版本
    print('\x1b[32mserial.version:{}'.format(serial.__version__), '\x1b[37m')
    
    # 端口/波特率/编码格式
    if get_os() == 'Windows':
        port = 'COM3'
        print('\x1b[36m命令行示例: python serial_monitor.py -p COM3 -b 115200 -f utf-8\x1b[37m')
    elif get_os() == 'Linux':
        port = '/dev/ttyUSB0'
        print('\x1b[36m命令行示例: sudo python serial_monitor.py -p /dev/ttyUSB0 -b 115200 -f utf-8\x1b[37m')
    elif get_os() == 'macOS':
        port = 'COM3'
        print('\x1b[36m命令行示例: python serial_monitor.py -p COM3 -b 115200 -f utf-8\x1b[37m')
        
    print('\x1b[36m波特率示例: 9600 19200 38400 57600 115200 等等')
    print('\x1b[36m编码格式示例: ansi(兼容gb2312) gbk(兼容gb2312) utf-8')

    for index, string in enumerate(sys.argv):
        if (string == '-p'):
            if len(sys.argv) > (index + 1) and sys.argv[index + 1] != '-b' and sys.argv[index + 1] != '-f':
                port = sys.argv[index + 1]
            else:
                print('\x1b[31m输入的端口名称错误\x1b[37m')
                run_flag = 0
        elif string == '-b':
            if len(sys.argv) > (index + 1) and sys.argv[index + 1].isnumeric() and sys.argv[index + 1] != '-p' and sys.argv[index + 1] != '-f':
                baudrate = int(sys.argv[index + 1])
            else:
                print('\x1b[31m输入的波特率错误\x1b[37m')
                run_flag = 0
        elif string == '-f':
            if len(sys.argv) > (index + 1) and sys.argv[index + 1] != '-p' and sys.argv[index + 1] != '-b':
                encoding = sys.argv[index + 1]
            else:
                print('\x1b[31m输入的编码名称错误\x1b[37m')
                run_flag = 0
            
    print('\x1b[32m端口:', port, '波特率:', baudrate, '编码:', encoding, '\x1b[37m')
    if get_os() == 'Windows':
        print('\x1b[32m快捷键[ 退出--ctrl+] or crtl+z or crtl+c or 输入‘exit’+回车 ] \x1b[37m')
    elif get_os() == 'Linux':
        print('\x1b[32m快捷键[ 退出--crtl+z or crtl+c or 输入‘exit’+回车 ] \x1b[37m')
        
    # 注册热键回调
    if get_os() == 'Windows':
        keyboard.add_hotkey('ctrl+]', hotkey_ctrl_close_callBack)
        keyboard.add_hotkey('ctrl+z', hotkey_ctrl_close_callBack)
    
    # 打开串口
    ser = serial.Serial(port)

    # 配置串口参数（例如：波特率为 9600）
    ser.baudrate = baudrate
    ser.parity = serial.PARITY_NONE
    ser.stopbits = 1
    ser.bytesize = 8
    ser.timeout = None  # 无超时，阻塞读取
    
    if not ser.is_open:
        print('\x1b[31m无法打开串口\x1b[37m')
        sys.exit(1)
        
    # 启动子线程
    if (run_flag == 1):
        thread_read.start()
        thread_write.start()

    # while 循环
    try:
        while True:
            if (run_flag == 0):
                if ser.is_open:
                    ser.close()
                break
                
            time.sleep(1)
            
    # ctrl+c 按下
    except KeyboardInterrupt:
        print('\x1b[33m快捷键按下: ctrl+c\x1b[37m')
        run_flag = 0
        pass
        
    run_flag = 0
    
    print('\n\x1b[33m退出\x1b[37m')
    
    # 按键模拟:回车
    if get_os() == 'Windows':
        keyboard.press('enter')
        
    # 关闭串口连接
    if ser.is_open:
        ser.close()

if __name__ == '__main__':
    main()

